#ifndef BITMAP_HPP
#define BITMAP_HPP

#include <algorithm>
#include <stdexcept>
#include <limits>
#include "location.hpp"

// template<int R, int C >
class bitmap
{
  typedef unsigned int field_t;
  enum 
  {
    field_size = sizeof(field_t),
    max_rows = 512,
    max_cols = 512,
    field_cols = max_cols / field_size + (max_cols % field_size != 0),
    bitmap_size = max_rows * field_cols,
    bitmap_byte_size = bitmap_size * field_size
  };

  typedef field_t bm_type[ bitmap_size ];

public:
  bitmap()
    : rows(0)
    , cols(0)
  {
    clear(0);
  }
  
  bitmap(const bitmap& bm)
  {
    rows = bm.rows;
    cols = bm.cols;
    std::copy(bm._bitmap, bm._bitmap + bitmap_size, _bitmap );
  }

  void operator = (const bitmap& bm)
  {
    rows = bm.rows;
    cols = bm.cols;
    std::copy(bm._bitmap, bm._bitmap + bitmap_size, _bitmap );
  }
  void clear(field_t v)
  {
    if (v!=0)
      v = std::numeric_limits<field_t>::max();
    std::fill(_bitmap, _bitmap + bitmap_size, v);
  }

  void create(int r, int c, field_t v)
  {
    if ( c > max_cols)
      throw std::out_of_range("bitmap create");
    rows = r;
    cols = c;
    this->clear(v);
  }
  
  bitmap& inverse()
  {
    for (int i=0 ; i < bitmap_size; ++i)
      _bitmap[i] = ~(_bitmap[i]);
    return *this;
  }

  bool operator ==( const bitmap& bm)
  {
    for (int i=0 ; i < bitmap_size; ++i)
      if ( _bitmap[i] != bm._bitmap[i] ) 
        return false;
    return true;
  }
  
  bool operator!=( const bitmap& bm)
  {
    return !this->operator == (bm);
    /*
    for (int i=0 ; i < bitmap_size; ++i)
      if ( _bitmap[i] != bm._bitmap[i] ) 
        return false;
    return *this;
    */
  }

  bitmap& operator &=( const bitmap& bm)
  {
    for (int i=0 ; i < bitmap_size; ++i)
      _bitmap[i] &= bm._bitmap[i];
    return *this;
  }

  bitmap& operator |=( const bitmap& bm)
  {
    for (int i=0 ; i < bitmap_size; ++i)
      _bitmap[i] |= bm._bitmap[i];
    return *this;
  }

  bitmap& operator ^=( const bitmap& bm)
  {
    for (int i=0 ; i < bitmap_size; ++i)
      _bitmap[i] ^= bm._bitmap[i];
    return *this;
  }

  bool _get ( int row, int col) const
  {
    int pcol = col / field_size;
    field_t f = _bitmap[row*field_cols + pcol];
    int bit = col - pcol*field_size;
    return ( f >> (field_size - bit) ) & 1;
  }

  bool get( location loc ) const { return _get( loc.row, loc.col); }


  void _set( location loc, bool val)
  {
    int pcol = loc.col / field_size;
    field_t f = _bitmap[loc.row*field_cols + pcol];
    int bit = loc.col - pcol*field_size;
    if ( val )
      f |= 1 << (field_size - bit);
    else
      f &=~(1 << (field_size - bit));
    _bitmap[loc.row*field_cols + pcol] = f;
  }

  void set( location loc ) { this->_set( loc, true); }
  void reset( location loc ) { this->_set( loc, false); }

  void set( location loc, double radius ) { this->_set( loc, true, radius); }
  void reset( location loc, double radius ) { this->_set( loc, false, radius); }
  
  void _set ( location loc, bool val, double radius)
  {
    
    int fr = loc.row - radius;
    int tr = loc.row + radius;
    int fc = loc.col - radius;
    int tc = loc.col + radius;

    for ( int i = fr; i <= tr; ++i)
    {
      for ( int j = fc; j <= tc; ++j)
      {
        location cur(i, j);
        if (cur.row < 0) cur.row += rows;
        if (cur.row >= rows ) cur.row -= rows;
        if (cur.col < 0) cur.col += cols;
        if (cur.col >= cols ) cur.col -= cols;

        if ( location::distance( loc, cur, location(rows, cols) ) <= radius )
        {
          this->_set(cur, val);
        }
      }
    }
  }

  void show(std::ostream& os, std::string text = "") const
  {
    os << "-------------------------- bitmap "<< text<< "------------------ " << std::endl;
    for ( int i = 0; i < rows; ++i)
    {
      for ( int j = 0; j < cols; ++j)
      {
        if ( get(location(i, j)) )
          os << '*';
        else
          os << '-';
      }
      os << std::endl;
    }
    os << std::endl;
  }

private:
  int rows, cols;
  bm_type _bitmap;
};

#endif

